#include "tetattds.h"
#include <gba.h>
#include "fieldgraphics.h"
#include "baseblock.h"
#include "playfield.h"
#include "util.h"
#include "sprite.h"
#include "marker.h"

#include "blocks_bin.h"
#include "blocks_pal_bin.h"
#include "sprites_bin.h"
#include "sprites_pal_bin.h"
#include "background_bin.h"
#include "background_map_bin.h"
#include "background_pal_bin.h"

#include "font_bin.h"
#include "font_pal_bin.h"

// map baseblocks, the same are used for both main and sub
// 32 blocks of 0x800 bytes each starting at 0x6000000
#define BACKGROUND_MAP_BASE  0  // start = 0x6000000, size = 32*32*2 = 0x00800
#define BLOCKS1_MAP_BASE     1  // start = 0x6000800, size = 32*32*2 = 0x00800
#define BLOCKS2_MAP_BASE     2  // start = 0x6001000, size = 32*32*2 = 0x00800
#define CONSOLE_MAP_BASE     3  // start = 0x6001800, size = 32*32*2 = 0x00800
#define TEXT_MAP_BASE        4  // start = 0x6002000, size = 32*32*2 = 0x00800

// tile baseblocks, the same are used for both main and sub
// 4 blocks of 0x4000 bytes each starting at 0x6000000
#define BLOCKS_TILE_BASE     1  // start = 0x6004000, size = 16 KB = 0x4000
#define TEXT_TILE_BASE       2  // start = 0x6008000, size = 4 KB  = 0x1000
#define BACKGROUND_TILE_BASE 3  // start = 0x600C000, size = ?

// map offsets etc
#define BLOCKMAP_STRIDE 32
#define TEXTMAP_STRIDE 32
#define SCORE_TEXT_OFFSET (3*TEXTMAP_STRIDE + 12)
#define TIME_TEXT_OFFSET (7*TEXTMAP_STRIDE + 12)
#define STOP_TIME_TEXT_OFFSET (11*TEXTMAP_STRIDE + 12)
#define COUNTDOWN_TEXT_OFFSET (5*TEXTMAP_STRIDE + 5)

FieldGraphics* g_fieldGraphics = NULL;

int hblankScrollCounter;
int hblankScroll;

void irqHBlank()
{
// 	int scroll = PLAYFIELD_OFFSET_Y ;
// 	scroll += (g_fieldGraphics) ? g_fieldGraphics->scrollOffset : 0;
// 	scroll -= (REG_VCOUNT-scroll)/12 * 4;
// 	REG_BG1VOFS = -scroll;
// 	REG_BG2VOFS = -scroll;
	if(REG_VCOUNT == 0) {
		hblankScroll = -PLAYFIELD_OFFSET_Y - g_fieldGraphics->scrollOffset;
		hblankScrollCounter = -hblankScroll + BLOCKSIZE;
		REG_BG1VOFS = hblankScroll;
		REG_BG2VOFS = hblankScroll;
	}

	if(hblankScrollCounter-- == 1) {
		hblankScroll += 4;
		REG_BG1VOFS = hblankScroll;
		REG_BG2VOFS = hblankScroll;
		hblankScrollCounter = BLOCKSIZE;
	}
}

void FieldGraphics::InitGraphics()
{
	Decompress(BG_PALETTE, background_pal_bin);
	Decompress(TILE_BASE_ADR(BLOCKS_TILE_BASE), blocks_bin);
	Decompress(TILE_BASE_ADR(BACKGROUND_TILE_BASE), background_bin);
	Decompress(TILE_BASE_ADR(TEXT_TILE_BASE), font_bin);	
	Decompress(SPRITE_GFX, blocks_bin);
	Decompress(SPRITE_GFX+0x2000, sprites_bin);
	Decompress(SPRITE_PALETTE, sprites_pal_bin);
	memset(MAP_BASE_ADR(BLOCKS1_MAP_BASE), 0, 0x800);
	memset(MAP_BASE_ADR(BLOCKS2_MAP_BASE), 0, 0x800);
	memset(MAP_BASE_ADR(TEXT_MAP_BASE), 0, 0x800);

	int size;
	u16* bgmap = (u16*)Decompress(background_map_bin, &size);
	u16* src = bgmap;
	u16* dest = (u16*)MAP_BASE_ADR(BACKGROUND_MAP_BASE);
	for(int y = 0; y < 160/8; y++) {
		for(int x = 0; x < 240/8; x++) {
			dest[x + y*256/8] = *src++;
		}
	}
	free(bgmap);

	// video mode
	REG_DISPCNT = MODE_0 | BG_ALL_ON | OBJ_ON | OBJ_1D_MAP | WIN0_ON;
		
	// background
	REG_BG0CNT =
		TEXTBG_SIZE_256x256 |
		BG_TILE_BASE(BACKGROUND_TILE_BASE) |
		BG_MAP_BASE(BACKGROUND_MAP_BASE) |
		BG_PRIORITY(BACKGROUND_PRIORITY) |
		BG_256_COLOR;

	// blocks 1
	REG_BG1CNT =
		TEXTBG_SIZE_256x256 |
		BG_TILE_BASE(BLOCKS_TILE_BASE) |
		BG_MAP_BASE(BLOCKS1_MAP_BASE) |
		BG_PRIORITY(BLOCKS_PRIORITY) |
		BG_256_COLOR;

	REG_BG1HOFS = -PLAYFIELD_OFFSET_X;

	// blocks 2
	REG_BG2CNT =
		TEXTBG_SIZE_256x256 |
		BG_TILE_BASE(BLOCKS_TILE_BASE) |
		BG_MAP_BASE(BLOCKS2_MAP_BASE) |
		BG_PRIORITY(BLOCKS_PRIORITY) |
		BG_256_COLOR;

	REG_BG2HOFS = -PLAYFIELD_OFFSET_X - 4;

	// text
	REG_BG3CNT =
		TEXTBG_SIZE_256x256 |
		BG_TILE_BASE(TEXT_TILE_BASE) |
		BG_MAP_BASE(TEXT_MAP_BASE) |
		BG_PRIORITY(TEXT_PRIORITY) |
		BG_256_COLOR;

	REG_WIN0H =
		(PLAYFIELD_OFFSET_X << 8) |
		(PLAYFIELD_OFFSET_X + BLOCKSIZE*PF_WIDTH);
	REG_WIN0V =
		(PLAYFIELD_OFFSET_Y << 8) |
		(PLAYFIELD_OFFSET_Y + BLOCKSIZE*PF_VISIBLE_HEIGHT);
	REG_WININ =  BIT(0) | BIT(1) | BIT(2)  | BIT(3)  | BIT(4);
	REG_WINOUT = BIT(0) | BIT(3)  | BIT(4);

	irqSet(IRQ_HBLANK, irqHBlank);
	irqEnable(IRQ_HBLANK);
}

FieldGraphics::FieldGraphics()
:	effects(),
	marker(GetMarker())
{
}

FieldGraphics::~FieldGraphics()
{
	delete marker;
}

void FieldGraphics::Draw(PlayField *pf)
{
	PrintScore(pf->GetScore());
	PrintTime(pf->GetTimeTicks());
	PrintStopTime(pf->GetScrollPause());

	DrawField(pf);
	DrawEffects();
	DrawMarkers(pf);
}

void FieldGraphics::DrawField(PlayField *pf)
{
	scrollOffset = (int)pf->GetScrollOffset();

	int i = PF_FIRST_BLOCK_FIRST_VISIBLE_ROW;

	for(int y = 0; y < PF_VISIBLE_HEIGHT+1; y++)
	{
		for(int x = 0; x < PF_WIDTH; x++)
		{
			BaseBlock* block = pf->GetField(i);
			
			//bool shaded = (i >= PF_FIRST_BLOCK_LAST_ROW) || (pf->GetState() == PFS_DEAD);
			
			int tile = (block != NULL && !block->IsState(BST_MOVING)) ?
				block->GetTile() : TILE_BLANK;
			
			tile <<= 2;
			//if (shaded)
			//	tile |= (1 << 12);
			
			u16* cell = (x%2) ? (u16*)MAP_BASE_ADR(BLOCKS2_MAP_BASE) : (u16*)MAP_BASE_ADR(BLOCKS1_MAP_BASE);
			cell += x*3/2 + y*BLOCKMAP_STRIDE*2;
			cell[0] = tile;
			cell[1] = tile+1;
			cell[BLOCKMAP_STRIDE] = tile+2;
			cell[BLOCKMAP_STRIDE+1] = tile+3;

			i++;
		}
	}
}

void FieldGraphics::DrawMarkers(PlayField *pf)
{
	PFState state = pf->GetState();
	if(state == PFS_PLAY || state == PFS_START)
	{
		int markerPos = pf->GetMarkerPos();
		marker->SetPos(GetFieldX(markerPos)-4, GetFieldY(markerPos, true)-2);
		marker->Draw();
	}
	else
	{
		marker->Disable();
	}
}

void FieldGraphics::PrintScore(int score)
{
	char str[9+1];
	snprintf(str, sizeof(str), "%9i", score);
	PrintLarge(SCORE_TEXT_OFFSET, str);
}

void FieldGraphics::PrintTime(int ticks)
{
	int seconds = ticks/60;
	char str[9+1];
	snprintf(str, sizeof(str), "%6i:%02i", seconds / 60, seconds % 60);
	PrintLarge(TIME_TEXT_OFFSET, str);
}

void FieldGraphics::PrintCountdown(int count)
{
	char str[2];
	if(count == 0)
		snprintf(str, sizeof(str), " ");
	else
		snprintf(str, sizeof(str), "%i", count);
	PrintLarge(COUNTDOWN_TEXT_OFFSET, str);
}

void FieldGraphics::PrintStopTime(int ticks)
{
	char str[9+1];
	snprintf(str, sizeof(str), "%6i.%02i", ticks/60, ticks*100/60%100);
	PrintLarge(STOP_TIME_TEXT_OFFSET, str);
}

int FieldGraphics::LargeCharTile(char c)
{
	switch (c)
	{
	case ':':  return TILE_LARGE_COLON;
	case '\'': return TILE_LARGE_TICK;
	case '"':  return TILE_LARGE_DOUBLE_TICK;
	case '.':  return TILE_LARGE_FULL_STOP;
	default:
		if(c >= '0' && c <= '9')
			return c - '0' + TILE_LARGE_ZERO;
		else
			return TILE_LARGE_SPACE;
	}	
}

void FieldGraphics::PrintLarge(uint32_t cell, const char* text)
{
	ASSERT(text != NULL);
	
	while(*text != '\0')
	{
		int tile = LargeCharTile(*text);
		((u16*)MAP_BASE_ADR(TEXT_MAP_BASE))[cell] = tile;
		((u16*)MAP_BASE_ADR(TEXT_MAP_BASE))[cell+TEXTMAP_STRIDE] = tile + LARGE_DIGIT_TILE_STRIDE;
		text++;
		cell++;
	}
}

int FieldGraphics::SmallCharTile(char c)
{
	return (c >= ' ' && c <= '~') ? c - ' ' : 0;
}

void FieldGraphics::PrintSmall(uint32_t cell, const char* text)
{
	ASSERT(text != NULL);
	
	while(*text != '\0')
	{
		int tile = SmallCharTile(*text);
		((u16*)MAP_BASE_ADR(TEXT_MAP_BASE))[cell] = tile;
		text++;
		cell++;
	}
}
